package com.fly.test.restful.json;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;

import com.fly.core.utils.JsonBeanUtils;
import com.fly.core.utils.JsonNodeUtils;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ParseJson
{
    WebClient webClient = WebClient.builder().codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(-1)).build();
    
    String url = "https://blog.csdn.net/community/home-api/v1/get-business-list?page=1&size=5&businessType=blog&username=qq_16127313";
    
    /**
     * 格式化后解析
     * 
     * @throws IOException
     */
    @Test
    public void testPrettyThenParse()
        throws IOException
    {
        // 格式化后按行读取
        String pretty = JsonNodeUtils.jsonPrettyPrint(getJsonData());
        List<String> lines = IOUtils.readLines(new StringReader(pretty));
        lines.stream().forEach(log::info);
        
        // jackson格式化后会在:前后添加空格
        lines.stream()
            .filter(line -> StringUtils.containsAny(line, "\"url\"", "\"viewCount\""))
            .map(line -> StringUtils.deleteWhitespace(line))
            .map(line -> StringUtils.defaultIfEmpty(StringUtils.substringBetween(line, ":\"", "\""), StringUtils.substringBetween(line, ":", ",")))
            .forEach(log::info);
    }
    
    /**
     * 格式化后解析
     * 
     * @throws IOException
     */
    @Test
    public void testPrettyThenParse002()
        throws IOException
    {
        // 数据格式化-jackson格式化后会在:前后添加空格
        String pretty = JsonNodeUtils.jsonPrettyPrint(getJsonData());
        List<String> lines = IOUtils.readLines(new StringReader(pretty));
        
        // 获取url
        lines.stream().filter(line -> StringUtils.contains(line, "\"url\"")).map(line -> StringUtils.substringBetween(StringUtils.deleteWhitespace(line), ":\"", "\"")).collect(Collectors.toList()).stream().forEach(log::info);
        
        // 获取viewCount
        lines.stream().filter(line -> StringUtils.contains(line, "\"viewCount\"")).map(line -> StringUtils.substringBetween(StringUtils.deleteWhitespace(line), ":", ",")).collect(Collectors.toList()).stream().forEach(log::info);
    }
    
    /**
     * 解析为实体
     * 
     * @throws IOException
     */
    @Test
    public void testToEntity()
        throws IOException
    {
        // json->blogData
        BlogData blogData = JsonBeanUtils.jsonToBean(getJsonData(), BlogData.class);
        log.info("blogData: {} ", blogData);
        
        // blogData->json
        String json = JsonBeanUtils.beanToJson(blogData);
        log.info("json: {} ", json);
    }
    
    /**
     * 转换为xml解析
     * 
     * @throws IOException
     * @throws DocumentException
     */
    @Test
    public void testToXmlThenParse()
        throws IOException, DocumentException
    {
        // json转xml
        String xml = JsonNodeUtils.jsonNodeToXml(JsonNodeUtils.jsonToJsonNode(getJsonData()));
        
        // dom4j、XPath
        Document doc = new SAXReader().read(new StringReader(xml));
        doc.selectNodes("//data/list/url").stream().forEach(n -> log.info(n.getStringValue()));
        doc.selectNodes("//data/list/viewCount").stream().forEach(n -> log.info(n.getStringValue()));
    }
    
    /**
     * 替换字符串方式解析json
     * 
     * @throws IOException
     */
    @Test
    public void testPrintUrls()
        throws IOException
    {
        String resp = webClient.get().uri("https://00fly.online/upload/data.json").acceptCharset(StandardCharsets.UTF_8).accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(String.class).block();
        String jsonBody = resp.replace("{", "{\n").replace("}", "}\n").replace(",", ",\n");
        try (InputStream is = new ByteArrayInputStream(jsonBody.getBytes(StandardCharsets.UTF_8)))
        {
            IOUtils.readLines(is, StandardCharsets.UTF_8)
                .stream()
                .filter(line -> StringUtils.contains(line, "\"url\""))
                .map(line -> StringUtils.substringBetween(StringUtils.deleteWhitespace(line), ":\"", "\""))
                .collect(Collectors.toList())
                .forEach(System.out::println);
        }
    }
    
    /**
     * 获取jsonData
     * 
     * @return
     * @throws IOException
     */
    private String getJsonData()
        throws IOException
    {
        String jsonBody;
        try
        {
            jsonBody = webClient.get()
                .uri(url)
                .acceptCharset(StandardCharsets.UTF_8)
                .accept(MediaType.APPLICATION_JSON)
                .retrieve()
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(10)) // 设置超时时间，避免长时间等待
                .block();
        }
        catch (Exception e)
        {
            log.error(e.getMessage(), e);
            Resource resource = new ClassPathResource("data.json");
            try (InputStream input = resource.getInputStream())
            {
                jsonBody = IOUtils.toString(input, StandardCharsets.UTF_8);
            }
        }
        return jsonBody;
    }
}

@Data
class BlogData
{
    private Integer code;
    
    private String message;
    
    private String traceId;
    
    private Record data;
}

@Data
class Record
{
    private List<SubList> list;
    
    private Long total;
}

@Data
class SubList
{
    String articleId;
    
    String title;
    
    String description;
    
    String url;
    
    Integer type;
    
    String top;
    
    String forcePlan;
    
    Long viewCount;
    
    Long commentCount;
    
    String editUrl;
    
    String postTime;
    
    Long diggCount;
    
    String formatTime;
    
    Object picList;
    
    Long collectCount;
}
